package com.canaan.business.service.bill.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.json.JSONUtil;
import com.canaan.business.common.enums.inOutStock.InOrderStatusEnum;
import com.canaan.business.common.enums.inOutStock.InStockOrderTypeEnum;
import com.canaan.business.domain.basic.WmsProduct;
import com.canaan.business.domain.bill.AdjustInBill;
import com.canaan.business.domain.bill.AdjustInBillDetail;
import com.canaan.business.domain.bill.BillDetailRecord;
import com.canaan.business.domain.inOutStock.WmsInStockOrder;
import com.canaan.business.domain.vo.*;
import com.canaan.business.mapper.bill.AdjustInBillMapper;
import com.canaan.business.service.basic.IWmsProductService;
import com.canaan.business.service.bill.IAdjustInBillDetailService;
import com.canaan.business.service.bill.IAdjustInBillService;
import com.canaan.business.service.bill.IBillDetailRecordService;
import com.canaan.business.service.inOutStock.IWmsInStockOrderService;
import com.canaan.common.constant.PrefixConstants;
import com.canaan.common.core.redis.RedisCache;
import com.canaan.common.encrypt.PushEdiService;
import com.canaan.common.exception.ServiceException;
import com.canaan.common.utils.DateUtils;
import com.canaan.common.utils.SecurityUtils;
import com.canaan.common.utils.StringUtils;
import com.canaan.common.utils.TableNoUtil;
import com.canaan.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 调拔入库单Service业务层处理
 *
 * @author ruoyi
 * @date 2023-02-01
 */
@Slf4j
@Service
public  class AdjustInBillServiceImpl implements IAdjustInBillService {
    @Resource
    private AdjustInBillMapper adjustInBillMapper;
    @Resource
    private IWmsInStockOrderService wmsInStockOrderService;
    @Resource
    private IWmsProductService productService;
    @Resource
    private IAdjustInBillDetailService adjustInBillDetailService;
    @Resource
    private IBillDetailRecordService billDetailRecordService;
    @Resource
    private ISysConfigService sysConfigService;
    @Resource
    private RedisCache redisCache;

    /**
     * 查询调拔入库单
     *
     * @param id 调拔入库单主键
     * @return 调拔入库单
     */
    @Override
    public AdjustInBill selectAdjustInBillById(Long id) {
        return adjustInBillMapper.selectAdjustInBillById(id);
    }

    /**
     * 查询调拔入库单列表
     *
     * @param adjustInBill 调拔入库单
     * @return 调拔入库单
     */
    @Override
    public List<AdjustInBill> selectAdjustInBillListPageDatas(AdjustInBill adjustInBill) {
        return adjustInBillMapper.selectAdjustInBillListPageDatas(adjustInBill);
    }

    /**
     * 查询调拔入库单列表
     *
     * @param adjustInBill 调拔入库单
     * @return 调拔入库单
     */
    @Override
    public List<AdjustInBill> selectAdjustInBillList(AdjustInBill adjustInBill) {
        return adjustInBillMapper.selectAdjustInBillList(adjustInBill);
    }

    /**
     * 新增调拔入库单
     *
     * @param adjustInBill 调拔入库单
     * @return 结果
     */
    @Override
    public int insertAdjustInBill(AdjustInBill adjustInBill) {
        if (StringUtils.isBlank(adjustInBill.getNo())) {
            adjustInBill.setNo(PrefixConstants.TRANSFER_RECEIPT+TableNoUtil.getNo(AdjustInBill.class));
        }
        if(StringUtils.isEmpty(adjustInBill.getCreateBy())){
            adjustInBill.setCreateBy(SecurityUtils.getUsername());
        }
        adjustInBill.setCreateTime(DateUtils.getNowDate());
        adjustInBill.setStatus(InOrderStatusEnum.NEW.getCode());
        return adjustInBillMapper.insertAdjustInBill(adjustInBill);
    }

    /**
     * 批量新增调拔入库单
     *
     * @param adjustInBillList 调拔入库单列表
     * @return 结果
     */
    @Override
    public int batchInsertAdjustInBill(List<AdjustInBill> adjustInBillList) {
        String username = SecurityUtils.getUsername();
        Date currerTime = DateUtils.getNowDate();
        for (AdjustInBill adjustInBill : adjustInBillList) {
            if (StringUtils.isBlank(adjustInBill.getNo())) {
                adjustInBill.setNo(TableNoUtil.getNo(AdjustInBill.class));
            }
            adjustInBill.setCreateBy(username);
            adjustInBill.setCreateTime(currerTime);
        }
        int insertNum = 0;
        for (int i = 0; i < adjustInBillList.size(); ) {
            int endIndex = i + 500;
            if (endIndex > adjustInBillList.size()) {
                endIndex = adjustInBillList.size();
            }
            insertNum = insertNum + adjustInBillMapper.batchInsertAdjustInBill(adjustInBillList.subList(i, endIndex));
            i = endIndex;
        }
        return insertNum;
    }

    /**
     * 修改调拔入库单
     *
     * @param adjustInBill 调拔入库单
     * @return 结果
     */
    @Override
    public int updateAdjustInBill(AdjustInBill adjustInBill) {
        if(StringUtils.isEmpty(adjustInBill.getUpdateBy())) {
            adjustInBill.setUpdateBy(SecurityUtils.getUsername());
        }
        adjustInBill.setUpdateTime(DateUtils.getNowDate());
        return adjustInBillMapper.updateAdjustInBill(adjustInBill);
    }

    /**
     * 批量修改调拔入库单
     *
     * @param adjustInBillList 调拔入库单列表
     * @return 结果
     */
    @Override
    public int batchUpdateAdjustInBill(List<AdjustInBill> adjustInBillList) {
        String username = SecurityUtils.getUsername();
        Date currerTime = DateUtils.getNowDate();
        for (AdjustInBill adjustInBill : adjustInBillList) {
            adjustInBill.setUpdateBy(username);
            adjustInBill.setUpdateTime(currerTime);
        }
        int updateNum = 0;
        for (int i = 0; i < adjustInBillList.size(); ) {
            int endIndex = i + 500;
            if (endIndex > adjustInBillList.size()) {
                endIndex = adjustInBillList.size();
            }
            updateNum = updateNum + adjustInBillMapper.batchUpdateAdjustInBill(adjustInBillList.subList(i, endIndex));
            i = endIndex;
        }
        return updateNum;
    }

    /**
     * 批量删除调拔入库单
     *
     * @param ids 需要删除的调拔入库单主键集合
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteAdjustInBillByIds(Long[] ids) {
        AdjustInBill bill = new AdjustInBill();
        bill.setIds(CollUtil.toList(ids));
        List<AdjustInBill> billList = this.selectAdjustInBillList(bill);
        List<String> nos = billList.stream()
                .filter(e -> InOrderStatusEnum.CANCEL_PENDING_CONFIRMATION.getCode().equals(e.getStatus())
                        || InOrderStatusEnum.ACCOMPLISH.getCode().equals(e.getStatus()))
                .map(AdjustInBill::getNo).collect(Collectors.toList());
        if(!nos.isEmpty()){
            throw new ServiceException("取消待确认或已完成状态的单据不能删除");
        }
        for (AdjustInBill adjustInBill : billList) {
            if(InOrderStatusEnum.PICKING.getCode().equals(adjustInBill.getStatus())){
                //校验进行中单据下的出入库单数据
                WmsInStockOrder stockOrder = wmsInStockOrderService.selectWmsInStockOrderByRelOrderNo(adjustInBill.getNo(), InStockOrderTypeEnum.ALLOCATE.getCode());
                if(stockOrder != null && stockOrder.getTotalActualQty() > 0){
                    nos.add(adjustInBill.getNo());
                }
            }
        }
        if(!nos.isEmpty()){
            throw new ServiceException(nos + "单据已有实际入库数量不能删除");
        }
        //入库单和入库单明细删除
        nos = billList.stream().map(AdjustInBill::getNo).collect(Collectors.toList());
        wmsInStockOrderService.deleteWmsInStockOrderByRelOrderNosAndType(nos, InStockOrderTypeEnum.ALLOCATE.getCode());
        return adjustInBillMapper.deleteAdjustInBillByIds(ids);
    }

    /**
     * 删除调拔入库单信息
     *
     * @param id 调拔入库单主键
     * @return 结果
     */
    @Override
    public int deleteAdjustInBillById(Long id) {
        return adjustInBillMapper.deleteAdjustInBillById(id);
    }

    /**
     * 查询调拔入库单
     *
     * @param no 编号
     * @return 调拔入库单
     */
    @Override
    public AdjustInBill selectAdjustInBillByNo(String no) {
        return adjustInBillMapper.selectAdjustInBillByNo(no);
    }

    /**
     * 批量删除调拔入库单
     *
     * @param nos 需要删除的编号集合
     * @return 结果
     */
    @Override
    public int deleteAdjustInBillByNos(String[] nos) {
        return adjustInBillMapper.deleteAdjustInBillByNos(nos);
    }

    /**
     * 删除调拔入库单信息
     *
     * @param no 编号
     * @return 结果
     */
    @Override
    public int deleteAdjustInBillByNo(String no) {
        return adjustInBillMapper.deleteAdjustInBillByNo(no);
    }

    @Override
    public int complete(List<Long> ids) {
        AdjustInBill bill = new AdjustInBill();
        bill.setIds(ids);
        List<AdjustInBill> billList = this.selectAdjustInBillList(bill);
        billList = billList.stream().filter(e -> InOrderStatusEnum.PICKING.getCode().equals(e.getStatus())).collect(Collectors.toList());
        if (billList.isEmpty()) {
            throw  new ServiceException("单据状态不是"+InOrderStatusEnum.PICKING.getMessage());
        }
        for (AdjustInBill adjustInBill : billList) {
            WmsInStockOrder wmsInStockOrder = wmsInStockOrderService.selectWmsInStockOrderByRelOrderNo(adjustInBill.getNo(), InStockOrderTypeEnum.ALLOCATE.getCode());
            if (wmsInStockOrder != null && InOrderStatusEnum.ACCOMPLISH.getCode().equals(wmsInStockOrder.getOrderStatus())) {
                adjustInBill.setCompleteTime(DateUtils.getNowDate());
                adjustInBill.setStatus(InOrderStatusEnum.ACCOMPLISH.getCode());
            } else {
                throw new ServiceException("入库单未完成，关联编号：" + adjustInBill.getNo());
            }
        }
        this.batchUpdateAdjustInBill(billList);
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int confirm(List<Long> ids) {
        AdjustInBill bill = new AdjustInBill();
        bill.setIds(ids);
        List<AdjustInBill> billList = this.selectAdjustInBillList(bill);
        billList = billList.stream().filter(e -> InOrderStatusEnum.NEW.getCode().equals(e.getStatus())).collect(Collectors.toList());
        if (billList.isEmpty()) {
            return 0;
        }
        billList.stream().forEach(e -> e.setStatus(InOrderStatusEnum.PICKING.getCode()));
        this.batchUpdateAdjustInBill(billList);

        //添加入库单
        for (AdjustInBill adjustInBill : billList) {
            AdjustInBillDetail adjustInBillDetail = new AdjustInBillDetail();
            adjustInBillDetail.setBillNo(adjustInBill.getNo());
            List<AdjustInBillDetail> adjustInBillDetails = this.adjustInBillDetailService.selectAdjustInBillDetailList(adjustInBillDetail);
            if (CollUtil.isEmpty(adjustInBillDetails)) {
                throw new ServiceException("未维护明细数据");
            }
            List<StockDetailProductVo> stockDetailProductVos = new ArrayList<>();
            for (AdjustInBillDetail billDetail : adjustInBillDetails) {
                StockDetailProductVo detailProductVo = new StockDetailProductVo();
                detailProductVo.setProductNo(billDetail.getProductNo());
                detailProductVo.setOrderQty(billDetail.getQty());
                stockDetailProductVos.add(detailProductVo);
            }
            wmsInStockOrderService.initBillStockOrder(adjustInBill.getNo(), InStockOrderTypeEnum.ALLOCATE.getCode(),null, stockDetailProductVos);
        }


        return billList.size();
    }

    @Override
    public List<AdjustInBillExportVo> exportList(AdjustInBill adjustInBill) {
        return adjustInBillMapper.exportList(adjustInBill);
    }

    @Override
    public List<String> importExcel(List<AdjustInBillExportVo> list) {
        List<String> errList = new ArrayList<>();
        Map<String, List<AdjustInBillExportVo>> groupMap = list.stream().collect(Collectors.groupingBy(e -> e.getAddress() + ":" + e.getConcat()));
        Set<String> groupKeys = groupMap.keySet();
        for (String groupKey : groupKeys) {
            String[] split = groupKey.split(":");
            String address = split[0];
            String concat = split[1];
            List<AdjustInBillExportVo> adjustInBillExportVos = groupMap.get(groupKey);
            AdjustInBill adjustInBill = new AdjustInBill();
            adjustInBill.setAddress(address);
            adjustInBill.setConcat(concat);
            adjustInBill.setTotalQty(adjustInBillExportVos.stream().filter(e->e.getQty()!=null).mapToLong(e -> e.getQty()).sum());
            this.insertAdjustInBill(adjustInBill);

            List<AdjustInBillDetail> details = new ArrayList<>();
            for (AdjustInBillExportVo adjustInBillExportVo : adjustInBillExportVos) {
                AdjustInBillDetail adjustInBillDetail = new AdjustInBillDetail();
                adjustInBillDetail.setBillNo(adjustInBillExportVo.getNo());
                adjustInBillDetail.setQty(adjustInBillExportVo.getQty());
                adjustInBillDetail.setProductNo(adjustInBillExportVo.getProductNo());
                WmsProduct wmsProduct = productService.selectWmsProductByProductNo(adjustInBillExportVo.getProductNo());
                if (wmsProduct == null) {
                    errList.add("无效物料编码：" + wmsProduct.getProductNo());
                    continue;
                }
                adjustInBillDetail.setProductName(wmsProduct.getProductName());
                adjustInBillDetail.setProductUnit(wmsProduct.getCompany());
                details.add(adjustInBillDetail);
            }
            if (CollUtil.isNotEmpty(details)) {
                adjustInBillDetailService.batchInsertAdjustInBillDetail(details);
            }
        }
        return errList;
    }

    @Override
    @Transactional
    public int cancel(List<Long> ids) {
        AdjustInBill bill = new AdjustInBill();
        bill.setIds(ids);
        List<AdjustInBill> billList = this.selectAdjustInBillList(bill);
        billList = billList.stream().filter(e -> InOrderStatusEnum.NEW.getCode().equals(e.getStatus())
                || InOrderStatusEnum.PICKING.getCode().equals(e.getStatus())).collect(Collectors.toList());
        if (billList.isEmpty()) {
            return 0;
        }
        for (AdjustInBill adjustInBill : billList) {
            WmsInStockOrder wmsInStockOrder = wmsInStockOrderService.selectWmsInStockOrderByRelOrderNo(adjustInBill.getNo(), InStockOrderTypeEnum.ALLOCATE.getCode());
            if (wmsInStockOrder!=null) {
                if ( wmsInStockOrder.getTotalActualQty() > 0){
                    throw new ServiceException("已入库不允许取消，单号：" + adjustInBill.getNo());
                }
                wmsInStockOrder.setOrderStatus(InOrderStatusEnum.CANCEL.getCode());
                this.wmsInStockOrderService.updateWmsInStockOrder(wmsInStockOrder);
            }
        }
        billList.stream().forEach(e -> e.setStatus(InOrderStatusEnum.CANCEL.getCode()));
        this.batchUpdateAdjustInBill(billList);
        return billList.size();
    }

    @Override
    public List<AdjustInBillExportCheckVo> exportCheckList(AdjustInBill adjustInBill) {
        return adjustInBillMapper.exportCheckList(adjustInBill);
    }

    /**
     * 确认取消
     * @param bill 单据信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int confirmCancel(AdjustInBill bill) {
        bill = this.selectAdjustInBillById(bill.getId());
        if(bill == null){
            throw new ServiceException("单据不存在");
        }else if(!InOrderStatusEnum.CANCEL_PENDING_CONFIRMATION.getCode().equals(bill.getStatus())){
            throw new ServiceException("该状态不是取消待确认，无法确认取消");
        }

        WmsInStockOrder stockOrder = wmsInStockOrderService.selectWmsInStockOrderByRelOrderNo(bill.getNo(), InStockOrderTypeEnum.ALLOCATE.getCode());
        if(stockOrder != null){
            stockOrder.setOrderStatus(InOrderStatusEnum.CANCEL.getCode());
            wmsInStockOrderService.updateWmsInStockOrder(stockOrder);
        }
        bill.setStatus(InOrderStatusEnum.CANCEL.getCode());
        return this.updateAdjustInBill(bill);
    }
    public void postInStockCallbackLock(String billNo,String stockStatus){
        InStockCallbackVo inStockCallbackVo = new InStockCallbackVo();
        inStockCallbackVo.setBizNo(billNo);
        inStockCallbackVo.setWmsBizNo(billNo);
        BillDetailRecord query=new BillDetailRecord();
        query.setBillNo(billNo);
        query.setDelFlag("0");
        List<BillDetailRecord> details=billDetailRecordService.selectBillDetailRecordList(query);
        Map<String,Long> map=new HashMap<>();
        List<GoodsVo> goodsVos=new ArrayList<>();
        for (BillDetailRecord item : details){
            if(!map.containsKey(item.getProductNo()+"_"+item.getProductUnit()+"_"+item.getBatchNo())){
                map.put(item.getProductNo()+"_"+item.getProductUnit()+"_"+item.getBatchNo(),item.getQty());
            }
            else{
                Long qty=map.get(item.getProductNo()+"_"+item.getProductUnit()+"_"+item.getBatchNo());
                map.put(item.getProductNo()+"_"+item.getProductUnit()+"_"+item.getBatchNo(),qty+item.getQty());
            }
        }
        for (String item: map.keySet()){
            String[] strAry=item.split("_");
            String productNo=strAry[0];
            String productUnit=strAry[1];
            String batchNo=strAry[2];
            GoodsVo goodsVo=new GoodsVo();
            goodsVo.setBatchNo(batchNo);
            goodsVo.setCode(productNo);
            goodsVo.setUnit(productUnit);
            goodsVo.setNum(map.get(item).toString());
            goodsVo.setStatus(Integer.parseInt(stockStatus));
            goodsVos.add(goodsVo);
        }
        inStockCallbackVo.setGoodDetails(goodsVos);
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        inStockCallbackVo.setDeliveryDate(simpleDateFormat.format(DateUtils.getNowDate()));
        // 发送入库回调
        if(Boolean.parseBoolean(sysConfigService.selectConfigByKey("edi.swtich"))){
            String str=JSONUtil.toJsonStr(inStockCallbackVo);
            log.info("inStock callback post = " + str);
            Pair<Boolean, String> result= PushEdiService.push(sysConfigService.selectConfigByKey("edi.inner"),str);
            log.info("inStock callback post result= " + result.getKey()+" msg="+result.getValue());
            if(!result.getKey()){
                throw new RuntimeException(result.getValue());
            }
        }
    }


    /**
     * 入库回调
     */
    public void postInStockCallback(String billNo,String stockStatus){
        String key = "IN_LOCK";
        //TODO 临时方案锁，后期处理
        String lock = redisCache.getCacheObject(key);
        if (StringUtils.equals(lock, "true")) {
            throw new RuntimeException("有入库任务正在进行中，请稍后再试");
        }
        redisCache.setCacheObject(key, "true");
        try {
            postInStockCallbackLock(billNo, stockStatus);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
            //TODO 临时方案
            redisCache.setCacheObject(key, "false");
        }
    }

    /**
     * 打印调拔入库单
     *
     * @param no
     * @return
     */
    @Override
    public AdjustInBillForPrintVo selectAdjustInBillForPrint(String no) {
        return adjustInBillMapper.selectAdjustInBillForPrint(no);
    }
}
